//
//  ACDiskStorage.m
//  ACCache_Example
//
//  Created by autohome on 2021/8/7.
//  Copyright © 2021 CaiXiang. All rights reserved.
//

#import "ACDiskStorage.h"
#import "FMDatabaseQueue.h"

static NSString *const kDBFileName = @"accache.db";
static NSString *const kCache = @"accache";

@implementation ACDiskStorage {
    NSString *_dbPath;
    FMDatabaseQueue *_dbQueue;
}

- (instancetype)initWithPath:(NSString *)path{
    if (path.length == 0) {
        return nil;
    }
    if (self = [super init]) {
        _dbPath = [path stringByAppendingPathComponent:kDBFileName];
        _dbQueue = [[FMDatabaseQueue alloc] initWithPath:_dbPath];
        [self _creatCacheDB];
        [self _timeClearData];
#if DEBUG
        NSLog(@"DBFile:🚀 %@", self->_dbPath);
#endif
    }
    return self;
}

- (void)_timeClearData {
    __weak typeof(self) _self = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        __strong typeof(_self) self = _self;
        if (!self) return;
        [self _timeClearInBackground];
        [self _timeClearData];
    });
}

- (void)_timeClearInBackground {
    [self removeExpiredCache];
}

- (void)dealloc {
    [_dbQueue close];
    _dbPath = nil;
    _dbQueue = nil;
#if DEBUG
    NSLog(@"%s",__func__);
#endif

}

#pragma mark - public
- (BOOL)saveCacheDatas:(NSArray <NSData *>*)datas forKey:(NSArray<NSString *> *)cacheKeys {
    __block NSMutableArray *array = [NSMutableArray array];
    [self _checkDBTable:YES];
    [_dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        for (NSInteger i = 0; i < datas.count; i ++) {
            __block BOOL isSuccess = NO;
            NSString *sqlStr = [NSString stringWithFormat:@"insert or replace into %@ (cacheKey, cacheData, usedTime) values (?,?,?)", kCache];
                isSuccess = [db executeUpdate:sqlStr,cacheKeys[i],datas[i],@(self.usedTime)];
            if (!isSuccess) {
                [array addObject:cacheKeys[i]];
                *rollback = YES;
            }
        }
    }];
    return !(array.count > 0);
}

- (BOOL)saveCacheData:(NSData *)data forKey:(NSString *)cacheKey {
   __block BOOL isSuccess = NO;
    [self _checkDBTable:YES];
    NSString *sqlStr = [NSString stringWithFormat:@"insert or replace into %@ (cacheKey, cacheData, usedTime) values (?,?,?)", kCache];
    [_dbQueue inDatabase:^(FMDatabase *db) {
        [db open];
        isSuccess = [db executeUpdate:sqlStr,cacheKey,data,@(self.usedTime)];
        if (!isSuccess) {
            NSLog(@"插入数据失败 - %@ - %@",cacheKey,@(self.usedTime));
        }
        [db close];
    }];
    return isSuccess;
}

- (NSData *)selectCacheData:(NSString *)cacheKey {
    __block NSData* data;
    if (![self _checkDBTable:NO]) {
        return nil;
    }
    [_dbQueue inDatabase:^(FMDatabase *db) {
        if ([db open]) {
            NSString *sqlStr = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE cacheKey = ?;", kCache];
            FMResultSet *reuslt = [db executeQuery:sqlStr,cacheKey];
            while ([reuslt next]) {
                NSData *statuData = [reuslt objectForColumn:@"cacheData"];
                data = statuData;
            }
            [db close];
        }
    }];
    return data;
}

- (BOOL)deleteAllCacheData {
    __block BOOL isSuccess = NO;
    if (![self _checkDBTable:NO]) {
        return YES;
    }
    [_dbQueue inDatabase:^(FMDatabase *db) {
        if ([db open]) {
            NSString *sqlStr = [NSString stringWithFormat:@"DELETE FROM %@", kCache];
            isSuccess = [db executeUpdate:sqlStr];
            [db close];
        }
    }];
    if (isSuccess) {
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            [db executeStatements:@"VACUUM"];
        }];
    }
    return isSuccess;
}

- (BOOL)deleteCacheData:(NSString *)cacheKey {
    __block BOOL isSuccess = NO;
    if (![self _checkDBTable:NO]) {
        return YES;
    }
    [_dbQueue inDatabase:^(FMDatabase *db) {
         if ([db open]) {
             [db beginTransaction];
             NSString *sqlStr = [NSString stringWithFormat:@"DELETE FROM %@ WHERE cacheKey = ?", kCache];
             isSuccess = [db executeUpdate:sqlStr,cacheKey];
             if (!isSuccess) {
                 [db rollback];
             }
             [db commit];
             [db close];
         }
     }];
    
    if (isSuccess) {
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            [db executeStatements:@"VACUUM"];
        }];
    }
    return isSuccess;
}

- (BOOL)existsCacheWithCacheKey:(NSString *)cacheKey {
    if (![self _checkDBTable:NO]) {
        return NO;
    }
    __block BOOL isExists = NO;
    NSString *sql = [NSString stringWithFormat:@"SELECT usedTime FROM %@ where cacheKey = ?;",kCache];
    [_dbQueue inDatabase:^(FMDatabase *db) {
        if ([db open]) {
            FMResultSet *resultSet = [db executeQuery:sql,cacheKey];
            isExists = resultSet.next;
            [resultSet close];
        }
    }];
    return isExists;
}

- (void)removeExpiredCache {
    NSTimeInterval interval = self.effectiveCacheTime;
    if (self.effectiveCacheTime == 0) {
        //代表不需要清理
        return;
    }
    NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
    NSTimeInterval oldestInterval = now - interval;
    __block BOOL isSuccess = NO;
    [_dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        if ([db open]) {
            NSString *sql = [NSString stringWithFormat:@"SELECT cacheKey from %@ where usedTime < ?;",kCache];
            FMResultSet *resultSet = [db executeQuery:sql,@(oldestInterval)];
            
            while (resultSet.next) {
                NSString *key = [resultSet objectForColumnIndex:0];
                NSString *sql1 = [NSString stringWithFormat:@"DELETE FROM %@ where cacheKey = ?;",kCache];
                isSuccess = [db executeUpdate:sql1,key];
                if (!isSuccess) {
                    *rollback = YES;
                }
            }
            
            [resultSet close];
        }
    }];
    if (isSuccess) {
        [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
            [db executeStatements:@"VACUUM"];
        }];
#if DEBUG
        NSLog(@"定时 清理缓存数据成功");
#endif
    }
}

#pragma mark - private
- (void)_creatCacheDB {
    [_dbQueue inDatabase:^(FMDatabase *db) {
        [db open];
        NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (cacheKey varchar PRIMARY KEY NOT NULL, cacheData blob NOT NULL,usedTime float NOT NULL);",kCache];
        [db executeUpdate:sql];
        [db close];
    }];
}

- (BOOL)_removeTable {
    __block BOOL tf = NO;
    if (![self _checkDBTable:NO]) {
        return YES;
    }
    [_dbQueue inDatabase:^(FMDatabase *db) {
        NSString *sql = [@"DROP TABLE " stringByAppendingString:kCache];
        tf = [db executeUpdate:sql,nil];
    }];
    return tf;
}

- (BOOL)_checkDBTable:(BOOL)newCreate {
    __block BOOL isExist;
    [_dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        NSString* sql = [NSString stringWithFormat:@"select count(*) from sqlite_master where type='table' and name='%@'", kCache];
        FMResultSet *rs = [db executeQuery:sql];
        
        while ([rs next]) {
            if ([rs intForColumnIndex:0] == 0) {
                //创建表
                if (newCreate) {
                    NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (cacheKey varchar PRIMARY KEY NOT NULL, cacheData blob NOT NULL,usedTime float NOT NULL);",kCache];
                    [db executeUpdate:sql];
                }
                [rs close];
                return;
            }
        }
        
        //表已存在
        isExist = YES;
        [rs close];
    }];
    return isExist;
}


#pragma mark - set&get
- (NSTimeInterval)usedTime {
    return [[NSDate date] timeIntervalSince1970];
}

@end
